import asyncio
import re
import urllib
from abc import ABC, abstractmethod

from playwright.async_api import async_playwright, Playwright, BrowserContext, expect, Page

from action import ActionType
from script import Script, ScriptInterpreter
from steps import BaseStep, ClickStep, GotoStep, InputStep, SleepStep, ScreenshotStep, WaitForSelectorStep, ExecStep, \
    JsEvaluateStep


# 新增 StepExecutor 基类，所有的执行器都继承自此类
class StepExecutor(ABC):
    @abstractmethod
    async def execute(self, page: Page, step: BaseStep):
        pass


# 每个操作类型的执行器
class ClickStepExecutor(StepExecutor):
    async def execute(self, page, step: ClickStep):
        await page.get_by_text("Connect Wallet").nth(0).click()


class GotoStepExecutor(StepExecutor):
    async def execute(self, page, step: GotoStep):
        await page.goto(step.url)


class InputStepExecutor(StepExecutor):
    async def execute(self, page, step: InputStep):
        await page.fill(step.selector, step.value)


class SleepStepExecutor(StepExecutor):
    async def execute(self, page, step: SleepStep):
        await asyncio.sleep(step.timeout)


class ScreenshotStepExecutor(StepExecutor):
    async def execute(self, page, step: ScreenshotStep):
        await page.screenshot(path=step.fileName)


class WaitForSelectorStepExecutor(StepExecutor):
    async def execute(self, page, step: WaitForSelectorStep):
        await page.wait_for_selector(step.selector, timeout=step.timeout)


class ExecStepExecutor(StepExecutor):
    async def execute(self, page, step: ExecStep):
        # 统一缩进处理
        code = "\n".join(f"    {line}" for line in step.expression.strip().splitlines())
        # 定义临时 async 函数
        func_code = f"async def ExecExecutorTemp(page):\n{code}"
        # 准备执行环境
        local_vars = {}
        exec(func_code, globals(), local_vars)
        # 调用
        await local_vars["ExecExecutorTemp"](page)


class JsEvaluateStepExecutor(StepExecutor):
    async def execute(self, page, step: JsEvaluateStep):
        result = await page.evaluate(step.expression)
        print("执行结果：", result)


# 定义一个 Executor 用于执行多个脚本
class Executor:
    def __init__(self, scripts: list[Script]):
        self.scripts = scripts
        self.step_executors = {
            ActionType.CLICK: ClickStepExecutor(),
            ActionType.GOTO: GotoStepExecutor(),
            ActionType.INPUT: InputStepExecutor(),
            ActionType.SLEEP: SleepStepExecutor(),
            ActionType.SCREENSHOT: ScreenshotStepExecutor(),
            ActionType.WAIT_FOR_SELECTOR: WaitForSelectorStepExecutor(),
            ActionType.EXEC: ExecStepExecutor(),
            ActionType.JSEVALUATE: JsEvaluateStepExecutor()
        }

    async def replaceInterpreter(self, page, interpreter: ScriptInterpreter):
        # 启用请求拦截
        await page.route(interpreter.match, lambda route, request: asyncio.create_task(handle_js(route, request)))

        async def handle_js(route, request):
            replace = None
            for replace_obj in interpreter.replaces:
                if re.match(replace_obj.url, request.url, re.DOTALL | re.S | re.M):
                    replace = replace_obj
                    break
            if replace:
                post_data = request.post_data
                headers = request.headers
                try:
                    print("请求url:", request.url)
                    print("请求体:", post_data)
                    print("请求头:", headers)
                    req = urllib.request.Request(request.url, data=post_data, headers=headers,
                                                 method=request.method)
                    with urllib.request.urlopen(req) as response:
                        content = response.read().decode(encoding=replace.encodeing)
                        content = re.sub(replace.match, replace.content, content,
                                         flags=re.DOTALL|re.MULTILINE|re.S)
                        await route.fulfill(status=response.status, content_type=response.info().get("Content-Type"),
                                            body=content.encode())
                        return
                except Exception as e:
                    print(e)

                # 例如：阻止请求（不加载该JS）
                # await route.abort()

                # 或者：重定向到空脚本，自己修改内容后响应
                # await route.fulfill(status=200, content_type="application/javascript", body="console.log('JS被拦截');")

                # 默认继续请求
            await route.continue_()

    async def run(self, browser: BrowserContext):
        for script in self.scripts:
            print(f"正在执行脚本: {script.name}")
            page = await browser.new_page()

            if script.interpreter:
                await self.replaceInterpreter(page, script.interpreter)

            for step in script.steps:
                print(f"正在执行步骤: {step.description}")
                executor = self.step_executors.get(step.action)
                if executor:
                    await executor.execute(page, step)
                else:
                    print(f"未找到操作类型: {step.action}")
            await browser.close()
